package cn.edu.xust.configuration.aop;

import cn.edu.xust.util.IpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

@Aspect
@Component
@Slf4j
public class AopLog {
    private static final String START_TIME = "请求-start";



    /**
     * 设置切入点
     * 切入controller包下面的所有Controller
     */

    @Pointcut("execution(public * cn.edu.xust.*.*Controller.*(..))")
    public void log() {
    }

    /**
     * 配置前置操作
     *
     * @param point
     */
    @Before("log()")
    public void beforeLog(JoinPoint point) throws IllegalAccessException, IOException {
        TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();


        //拿到请求
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        //拿到controller入参（这里不是body）
        String bodytxt = JSONUtil.toJsonStr(point.getArgs());

        //请求方式
        String method = request.getMethod();

        //处理ip地址查询
        String remortIP = IpUtil.getIpAddr(request);

        //ip位置
        String location = IpUtil.getLocationByIp(remortIP);

        //拿到params参数
        Map<String, String> map = new HashMap<>(16);
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String name = parameterNames.nextElement();
            map.put(name, request.getParameter(name));
        }


        //设置请求开始时间
        Long start = System.currentTimeMillis();
        request.setAttribute(START_TIME, start);
        log.info(" [AOP-LOG-START] [请求IP]{},[地理位置]{},[请求Method]{}[请求URL]{}[命中类]{}[方法名]{}[params参数]{}",
                remortIP, location.replaceAll("\\|", ":"), method, request.getRequestURL(), point.getSignature().getDeclaringTypeName(),
                point.getSignature().getName(), JSONObject.toJSONString(map));
        log.info("aopbody=>{}", bodytxt);

    }

    /**
     * 配置环绕操作
     *
     * @param point 切入点
     * @return 原方法返回值
     * @throws Throwable 异常信息
     */
    @Around("log()")
    public Object aroundLog(ProceedingJoinPoint point) throws Throwable {
        Object result = point.proceed();
        String s = JSONUtil.toJsonStr(result);
        s = s == null ? "" : s;
        log.info("MethodReturnData返回的数据({}):{}", s.length() > 4000 ? s.length() + "个字符,只记录4000字符" : s.length() + "个字符", s.length() > 4000 ? s.substring(0, 4000) : s);
        return result;
    }


    /**
     * 配置后置操作
     */
    @AfterReturning("log()")
    public void afterReturning() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        String header = request.getHeader("User-Agent");
        UserAgent userAgent = UserAgent.parseUserAgentString(header);
        Long start = (Long) request.getAttribute(START_TIME);
        Long end = System.currentTimeMillis();
        //客户端
        String clientname = "unknown".equalsIgnoreCase(userAgent.getOperatingSystem().toString()) ? header : userAgent.getOperatingSystem().toString();
        log.info("[AOP-LOG-END]" + " [耗时]" + (end - start) + " ms" + " [操作系统]" + clientname);
    }

    public static Map<String, Object> getKeyAndValue(Object obj) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<>(16);
        // 得到类对象
        Class<?> userCla = obj.getClass();
        //得到类中的所有属性集合
        Field[] fs = userCla.getDeclaredFields();
        for (int i = 0; i < fs.length; i++) {
            Field f = fs[i];
            // 设置些属性是可以访问的
            f.setAccessible(true);
            Object val = f.get(obj);
            // 得到此属性的值 设置键值
            map.put(f.getName(), val);

        }
        return map;
    }
}


